package cn.song.freight.controller;

import cn.hutool.json.JSONUtil;
import cn.song.freight.annotation.OperationLog;
import cn.song.freight.domain.dto.*;
import cn.song.freight.domain.entity.AreaDO;
import cn.song.freight.domain.result.PageUtils;
import cn.song.freight.domain.result.R;
import cn.song.freight.enums.ModuleType;
import cn.song.freight.enums.OperationType;
import cn.song.freight.service.AreaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;

/**
 * 区域管理控制器
 * <pre>
 * 该控制器管理运费计算和物流操作中的地理区域层级结构。
 * 支持三级行政区划系统：省 -> 市 -> 区。
 *
 * 核心功能：
 * - 层级化区域管理（省/市/区级别）
 * - 区域配置的CRUD操作
 * - 区域代码和名称标准化
 * - 灵活的查询和过滤功能
 * - 区域验证和数据一致性检查
 *
 * 区域结构：
 * - 等级 1：省份 - 顶级行政区划
 * - 等级 2：城市 - 省下级市级区划
 * - 等级 3：区县 - 市下级区县级区划
 *
 * @author SongRenShuo
 * @version 3.0.0
 * @since 2025-08-23
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/area")
@Api(tags = "区域管理")
@RequiredArgsConstructor
public class AreaController {

    /**
     * 区域管理服务，处理业务逻辑
     */
    private final AreaService areaService;

    /**
     * 在系统中创建新的区域
     * <pre>
     * 此端点在指定层级创建新的地理区域。
     * 系统会验证层级关系并确保适当的
     * 数据一致性和体系完整性。
     *
     * @param areaAddDTO 区域创建数据传输对象
     * @return 操作结果和创建的区域信息
     */
    @PostMapping("/add")
    @ApiOperation("创建新的区域")
    @OperationLog(
            moduleType = ModuleType.AREA,
            operationType = OperationType.INSERT,
            description = "新增区域信息",
            businessNameField = "provinceName",
            businessIdField = "id"
    )
    public R<Boolean> addArea(@Valid @RequestBody AreaAddDTO areaAddDTO) {
        log.info("创建新区域: 等级={}, 省份名称={}, 城市名称={}, 区县名称={}",
                areaAddDTO.getLevel(), areaAddDTO.getProvinceName(),
                areaAddDTO.getCityName(), areaAddDTO.getDistrictName());

        Boolean result = areaService.addArea(areaAddDTO);

        if (!result) {
            log.warn("区域创建失败: 等级={}", areaAddDTO.getLevel());
            return R.failed("区域创建失败");
        }

        log.info("区域创建成功: 等级={}", areaAddDTO.getLevel());
        return R.ok("区域创建成功");
    }

    /**
     * 从系统中删除现有区域
     * <pre>
     * 此端点在验证该区域当前未被任何
     * 运费策略使用后从系统中移除区域。
     * 应用级联删除规则以保持层级一致性。
     *
     * @param areaDeleteDTO 区域删除数据传输对象，包含要删除的区域ID
     * @return 操作结果，指示删除成功或失败
     */
    @PostMapping("/delete")
    @ApiOperation("删除现有区域")
    @OperationLog(
            moduleType = ModuleType.AREA,
            operationType = OperationType.DELETE,
            description = "删除区域信息",
            businessIdField = "areaId"
    )
    public R<Boolean> deleteArea(@Valid @RequestBody AreaDeleteDTO areaDeleteDTO) {
        log.info("删除区域: 区域ID={}", areaDeleteDTO.getAreaId());

        Boolean result = areaService.deleteArea(areaDeleteDTO);

        if (!result) {
            log.warn("区域删除失败: 区域ID={}", areaDeleteDTO.getAreaId());
            return R.failed("区域删除失败");
        }
        log.info("区域删除成功: 区域ID={}", areaDeleteDTO.getAreaId());
        return R.ok("区域删除成功");
    }

    /**
     * 更新现有区域的信息
     * <pre>
     * 此端点在保持层级结构和
     * 引用完整性的同时修改现有区域的详细信息。
     *
     * @param areaUpdateDTO 区域更新数据传输对象，包含修改后的区域信息
     * @return 操作结果，指示更新成功或失败
     */
    @PostMapping("/update")
    @ApiOperation("更新现有区域信息")
    @OperationLog(
            moduleType = ModuleType.AREA,
            operationType = OperationType.UPDATE,
            description = "更新区域信息",
            businessIdField = "areaId"
    )
    public R<Boolean> updateArea(@Valid @RequestBody AreaUpdateDTO areaUpdateDTO) {
        log.info("更新区域: 区域ID={}", areaUpdateDTO.getAreaId());

        Boolean result = areaService.updateArea(areaUpdateDTO);

        if (!result) {
            log.warn("区域更新失败: 区域ID={}", areaUpdateDTO.getAreaId());
            return R.failed("区域更新失败");
        }

        log.info("区域更新成功: 区域ID={}", areaUpdateDTO.getAreaId());
        return R.ok("区域更新成功");
    }

    /**
     * 使用过滤和排序选项查询区域
     * <pre>
     * 此端点根据提供的查询参数检索区域列表。
     * 支持按层级、编码、名称等多种条件进行过滤，
     * 并可按照层级结构进行排序。
     *
     * @param areaQueryDTO 区域查询数据传输对象，包含过滤条件
     * @return 符合条件的区域列表
     */
    @PostMapping("/list")
    @ApiOperation("查询区域列表")
    public R<List<AreaDO>> listAreas(@Valid @RequestBody AreaQueryDTO areaQueryDTO) {
        log.info("查询区域，过滤条件: {}", JSONUtil.toJsonStr(areaQueryDTO));

        // 为保持原接口兼容性，使用原有的查询方式
        List<AreaDO> areas = areaService.listAreasForCompatibility(areaQueryDTO);

        log.info("找到 {} 个符合条件的区域", areas.size());
        return R.success("区域查询成功", areas);
    }

    /**
     * 使用过滤和排序选项查询区域（分页版本 - 专用于页面）
     * <pre>
     * 此端点根据提供的查询参数检索区域列表。
     * 支持按层级、编码、名称等多种条件进行过滤，
     * 并可按照层级结构进行排序。支持分页查询。
     *
     * @param areaQueryDTO 区域查询数据传输对象，包含过滤条件和分页参数
     * @return 符合条件的分页区域列表
     */
    @PostMapping("/list-page")
    @ApiOperation("查询区域列表（分页）")
    public R<PageUtils<AreaDO>> listAreasWithPage(@Valid @RequestBody AreaQueryDTO areaQueryDTO) {
        log.info("查询区域（分页），过滤条件: {}",JSONUtil.toJsonStr( areaQueryDTO));

        PageUtils<AreaDO> pageResult = areaService.listAreas(areaQueryDTO);

        log.info("找到 {} 个符合条件的区域，当前页: {}/{}",
                pageResult.getTotalSize(), pageResult.getCurrentPage(), pageResult.getTotalPages());
        return R.success("区域查询成功", pageResult);
    }

    /**
     * 使用过滤和排序选项查询区域(树形结构)
     * <pre>
     * 此端点根据提供的查询参数检索区域列表，并以树形结构返回。
     * 支持按层级、编码、名称等多种条件进行过滤。
     *
     * @param areaQueryDTO 区域查询数据传输对象，包含过滤条件
     * @return 符合条件的区域树形结构列表
     */
    @PostMapping("/list-tree")
    @ApiOperation("查询区域列表(树形结构)")
    public R<List<AreaTreeDTO>> listAreasTree(@Valid @RequestBody AreaQueryDTO areaQueryDTO) {
        log.info("查询区域树形结构，过滤条件: {}", JSONUtil.toJsonStr(areaQueryDTO));

        List<AreaTreeDTO> areasTree = areaService.listAreasTree(areaQueryDTO);

        log.info("区域树形结构查询成功");
        return R.success("区域树形结构查询成功", areasTree);
    }


    /**
     * 根据ID获取区域信息
     * <pre>
     * 此端点根据提供的区域ID检索区域详细信息。
     *
     * @param requestDTO 区域ID请求数据传输对象
     * @return 区域详细信息
     */
    @PostMapping("/get-by-id")
    @ApiOperation("根据ID获取区域信息")
    public R<AreaDO> getAreaById(@Valid @RequestBody BaseIdDTO requestDTO) {
        log.info("根据ID获取区域信息，区域ID: {}", requestDTO.getId());

        AreaDO area = areaService.getById(requestDTO.getId());
        if (area == null) {
            log.warn("未找到指定ID的区域信息: {}", requestDTO.getId());
            return R.failed("未找到指定的区域信息");
        }

        log.info("区域信息获取成功: {}", area);
        return R.success("区域信息获取成功", area);
    }

    /**
     * 导出区域数据
     * <pre>
     * 此端点根据提供的查询参数导出区域数据为Excel文件。
     * 支持按层级、编码、名称等多种条件进行过滤后导出。
     *
     * @param areaQueryDTO 区域查询数据传输对象，包含过滤条件
     * @return 包含Excel数据字节的响应结果
     */
    @PostMapping("/export")
    @ApiOperation("导出区域数据")
    public R<byte[]> exportAreas(@RequestBody AreaQueryDTO areaQueryDTO) {
        try {
            log.info("导出区域数据，过滤条件: {}", JSONUtil.toJsonStr(areaQueryDTO));

            // 生成导出数据
            byte[] excelData = areaService.exportAreasToExcel(areaQueryDTO);

            log.info("区域数据导出成功，文件大小: {} 字节", excelData.length);
            return R.success("区域数据导出成功", excelData);
        } catch (Exception e) {
            log.error("区域数据导出异常", e);
            return R.failed("数据导出失败: " + e.getMessage());
        }
    }
}
